Skip to content

Require the Mcp-Method header on the modern path - #492

Merged
koic merged 1 commit into
modelcontextprotocol:mainfrom
koic:require_mcp_method_header_on_the_modern_path
Aug 8, 2026
Merged

Require the Mcp-Method header on the modern path#492
koic merged 1 commit into
modelcontextprotocol:mainfrom
koic:require_mcp_method_header_on_the_modern_path

Conversation

@koic

@koic koic commented Aug 7, 2026

Copy link
Copy Markdown
Member

Motivation and Context

The modern Streamable HTTP path validated the SEP-2243 mirror headers only when present: an absent Mcp-Method or Mcp-Name was silently tolerated. The 2026-07-28 specification requires Mcp-Method on every request and Mcp-Name on tools/call, resources/read, and prompts/get, and lists a missing required standard header among the validation failures a server MUST reject; the TypeScript SDK enforces presence with -32020 the same way. Tolerating absence also defeats the headers' purpose silently: they exist so intermediaries can route and inspect requests without parsing bodies.

A modern POST without Mcp-Method, or a name-bearing modern POST whose body carries a target name without Mcp-Name, now answers HTTP 400 with -32020 naming the missing header, exactly like a mismatch. Mcp-Name stays unrequired when the body carries no target name, matching the TypeScript SDK. The bundled MCP::Client::HTTP already sends both headers on every modern request, so SDK-to-SDK traffic is unaffected.

How Has This Been Tested?

New tests in test/mcp/server/transports/streamable_http_transport_test.rb cover the missing Mcp-Method and missing Mcp-Name rejections, asserting the -32020 code and the header name in the message. The modern_rack_request helper now mirrors a conforming client by deriving Mcp-Method from the body (with method_header: nil to omit it), so the existing modern-path tests exercise the requirement on every request.

Breaking Changes

None for conforming clients, which must already send these headers. Hand-rolled modern clients that omitted them are now rejected with -32020 instead of being served; add the headers, or use the legacy lifecycle, to proceed.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

## Motivation and Context

The modern Streamable HTTP path validated the SEP-2243 mirror headers only when present:
an absent `Mcp-Method` or `Mcp-Name` was silently tolerated. The 2026-07-28 specification
requires `Mcp-Method` on every request and `Mcp-Name` on `tools/call`, `resources/read`,
and `prompts/get`, and lists a missing required standard header among the validation failures
a server MUST reject; the TypeScript SDK enforces presence with `-32020` the same way.
Tolerating absence also defeats the headers' purpose silently: they exist so intermediaries
can route and inspect requests without parsing bodies.

A modern POST without `Mcp-Method`, or a name-bearing modern POST whose body carries
a target name without `Mcp-Name`, now answers HTTP 400 with `-32020` naming the missing header,
exactly like a mismatch. `Mcp-Name` stays unrequired when the body carries no target name,
matching the TypeScript SDK. The bundled `MCP::Client::HTTP` already sends both headers on
every modern request, so SDK-to-SDK traffic is unaffected.

## How Has This Been Tested?

New tests in `test/mcp/server/transports/streamable_http_transport_test.rb` cover
the missing `Mcp-Method` and missing `Mcp-Name` rejections, asserting the `-32020` code
and the header name in the message. The `modern_rack_request` helper now mirrors
a conforming client by deriving `Mcp-Method` from the body (with `method_header: nil` to omit it),
so the existing modern-path tests exercise the requirement on every request.

## Breaking Changes

None for conforming clients, which must already send these headers.
Hand-rolled modern clients that omitted them are now rejected with `-32020` instead of being served;
add the headers, or use the legacy lifecycle, to proceed.
@koic
koic merged commit 2c3c307 into modelcontextprotocol:main Aug 8, 2026
11 checks passed
@koic
koic deleted the require_mcp_method_header_on_the_modern_path branch August 8, 2026 06:29
koic added a commit that referenced this pull request Aug 8, 2026
## Motivation and Context

#490 and #492 merged independently green but broke each other on main: #492 made the modern path reject
a name-bearing POST whose body carries a target name without `Mcp-Name` (-32020, HTTP 400)
and taught the `modern_rack_request` helper to derive `Mcp-Method` from the body, while #490,
merged in between, added five modern `tools/call` tests that build their requests through that helper
and therefore send no `Mcp-Name`. Since the #492 merge every CI run on main fails those five tests
with 400 where 200 is expected; the library behavior itself is correct.

The helper now derives `Mcp-Name` the same way it derives `Mcp-Method`: from `params.name` or
`params.uri` when the body's method is one of the name-bearing three. That is what a conforming client sends per
the 2026-07-28 specification, and what the TypeScript SDK's client, the bundled `MCP::Client::HTTP`,
and the conformance harness's standard headers all do, so tests built through
the helper model a compliant client by default. `name_header: nil` omits the header for tests of
the requirement itself, symmetric with `method_header: nil`, and an explicit `headers:` entry still overrides
the derived value, which keeps the base64 mismatch tests exercising their divergent names.

## How Has This Been Tested?

The five failing tests pass again without being touched, and the #492 requirement tests
(missing `Mcp-Method`, missing `Mcp-Name`, header mismatches, base64 decoding) still pass:
the transport test file reports 217 runs with zero failures. `bundle exec rake` is green,
including RuboCop and the `--requirements 2025-11-25` conformance baseline.

## Breaking Changes

None. The change is confined to a test helper; the shipped gem is untouched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants